Tutorial: 01 - JS ES6

01 - JS ES6



1. Programación funcional

Una función puede retornar otra función, la sintaxis es algo extraña, pero es uno de los fundamentos de lo que conocemos como programación funcional, y se usa en react. En la práctica no tiene esta pinta, pero es importante tener la noción. En React se utiliza para crear componentes.

function hello() {
    return function () {
        return 'Hola mundo';
    }
}
const execHello = hello()();
console.log(execHello);


2. Parámetros múltiples y por defecto

  1. Podemos un if en la función para poder asignar un valor siempre.
  2. Para evitar el if, podemos pasar parámetros por defecto.
  3. Esto lo podemos hacer para cualquiera de los parámetros que incluyamos en la función.
function add(x, y, z=0) {
    if (y === undefined) {
        y = 0;
    }
    return x + y + z;
}

const execAdd = add(10, 6, 4);
console.log(execAdd);

console.log(add(10));

console.log(add(15));
console.log(add(15, 0, 5));


3. Objetos

3.1. ANATOMÍA BÁSICA DE OBJETOS EN JAVASCRIPT

Como ya sabemos, en un objeto tenemos propiedades y métodos. En este ejemplo de objeto, vemos que podemos asignar también una función. Hacemos varias llamadas a propiedades y al método por consola para ver distintas maneras de acceder, y cómo manejaríamos la sintaxis. En la segunda función de envío de email, utilizamos una sintaxis más simplificada, que también es válida.

const user = {
    name: 'Jose',
    apellido: 'Ochoteco',
    edad: 34,
    direccion: {
        pais:'España',
        ciudad:'Hondarribia',
        calle:'Jaizkibel etorbidea 8'
    },
    amigos  :[
        'Christian',
        'Villi'
    ],
    usuarioActivo: true,
    sendMail: function () { 
        return '...enviando email'
    },
    sendOtherMail () {
        return '...enviando otro email'
    }
};
console.log(user.sendMail());
console.log(user.sendOtherMail());
console.log(user.apellido);
console.log('Edad: ' + user.edad);

3.2. SHORTHAND PROPERTY NAMES

Podemos crear objetos a partir de otros datos. Podemos utilizar incluso una sintaxis más abreviada.

const nombre = 'laptop';
const price = 3000;
const newProduct = {
    nombre: nombre,
    price: price
}
const otherNewProduct = {
    nombre,
    price
}
console.log(newProduct);
console.log(otherNewProduct);


4. DOM y event handlers

Hemos metido algunos elementos en el DOM. Podemos añadirle una escucha, convirtiendo el elemento en un "manejador de eventos" o "event handler". --> Elemento que dispara al evento Le añadimos un "eventListener" o "escuchador de eventos" que viene a ser la función que escucha al evento --> Función que escucha al eventoTIP: cntrl + espacio, vscode te saca todos los eventos a los que puede escuchar la función que asignes

NOTA: Para que funcione el addEventListener, hay que meterlo al final. Temas de "hoisting", el orden en que JavaScript guarda en memoria las variables y los eventos, y pinta luego el DOM. Si este código estuviera debidamente separado en ficheros y/o clases JS, funcionaría correctamente. Al ser solo un ejemplo con fines didácticos no hay problema.

const title = document.createElement('h1');
title.innerText = 'Hola mundo desde JS';
const button = document.createElement('button');
button.innerText = 'Cambiar texto';
document.body.append(title);
document.body.append(button);
button.addEventListener('click', function () {
    console.log('Hola mundito');
    title.innerText = 'He cambiado el texto';
});


5. Destructuring

Cuando creamos funciones en JS podemos recibir objetos, los cuales pueden utilizarse dentro del DOM.

const persona = {
    name: 'David',
    edad: 40
};

function printSaludoPersona(persona) {
    return  `
    <h1>
        Hola ${persona.name}
    </h1>
    `;
}
document.body.innerHTML += printSaludoPersona(persona);

También podríamos acceder a propiedades con esta otra sintaxis

function printInfoPersona(persona) {
    return  `
    <h2>
        La edad de ${persona['name']} es ${persona['edad']}
    </h2>
    `;
}
document.body.innerHTML += printInfoPersona(persona);

Y esto sería el "Destructuring", que nos permite hacer varias cosas:

  1. Se utiliza para especificar propiedades de un objeto hipotético como parámetros. Es decir, indicamos que esperamos un objeto que tenga una propiedad en concreto, pero sin especificar que objeto.
  2. Le podemos pasar un parámetro, asumiendo que es un objeto, y luego dentro, extraer propiedades. Esto es similar la sintaxis de importación de módulos de node. Concluimos en que la destructuración se puede utilizar en cualquier parte del código.
function printSaludoEdadPersona({name, edad}) { // 1
    return  `
    <h2>
        Hola, soy ${name} y mi edad es ${edad}
    </h2>
    `;
}
document.body.innerHTML += printSaludoEdadPersona(persona);



function printDespedidaPersona(persona) { // 2
    const {name, edad} = persona;
    return  `
    <h2>
        Mi nombre es ${name}, tengo ${edad} años. Me despido de ustedes!
    </h2>
    `;
}
document.body.innerHTML += printDespedidaPersona(persona);


6. Funciones anónimas

En JavaScript no es necesario que las funciones tengan un nombre. Solo necesitamos retornarla en una variable, o autoejecutarla detrno de otro método o función predeterminado, o que hayamos creado. Aunque no parezca muy útil, es habitual utilizarlo cuando trabajamos con eventos.

A continuación, dos ejemplos sencillos, pero prácticos, que seguramente hemos utilizado más de una vez, y que usan funciones anónimas

console.log(function () {
    return 'starting'
}());
const boton = document.createElement('button');

boton.innerText = 'Haz click ';

boton.addEventListener('click', function () {
    console.log('Clicado!');
});
document.body.append(boton);


7. Arrow functions

7.1. ANATOMÍA ARROW FUNCTIONS

Las funciones arrow tienen esta forma. Veamos una función normal con su formato, y el equivalente como función arrow. También podemos ver ejemplificado cómo pasaríamos la función flecha a través de un eventListener.

function funcSuma(x, y) {
    return x + y;
}
const funcSumaArrow = (x, y) => {
    return x + y;
}

const boton2 = document.createElement('button');
boton2.innerText = 'otro botón';
document.body.append(boton2);

boton2.addEventListener('click', () => {
    console.log('Clicado botón 2!');
});

7.2. INLINE ARROW FUNCTIONS

Las arrow functions nos permiten retornar valores con una sintaxis abreviada, prescindiendo de las llaves. En el código que se indica, podemos ver varios ejemplos, con distintos valores (string, number, array). El único caso un poco tedioso es cuando queremos deveolver un objeto, ya que la sintaxis no concuerda, debido a que el objeto y la función utilizan llaves en su sintaxis, y se genera un conflicto a la hora de interpretar el código. Por ello utilizamos los paréntesis para pasar el objeto.

const texto = () => 'Texto';
const numero = () => 57;
const verdadero = () => true;
const agrupacion = () => [1,2,3,4];
const objeto = () => ({tipoValor: "objeto"});

console.log(texto());
console.log(numero());
console.log(verdadero());
console.log(agrupacion());
console.log(objeto());

7.3. RETURN EN ARROW FUNCTIONS

Return es una forma de acabar la ejecución de una función. No obstante, si lo incluimos dentro de una condicional, conseguimos dos cosas:

  1. Ahorrarnos el else de dicha condicional
  2. Parar la ejecución si se cumple esa condición

Aunque sea algo relativamente subjetivo, podríamos coincidir en que esta es una sintaxis más sencilla de leer. Esta es una práctica bastante utilizada en React a la hora de utilizar return.

const isIdentified = true;
const botonIdentificacion = document.createElement('button');
botonIdentificacion.innerText = 'Por favor, identifíquese';
document.body.append(botonIdentificacion);
botonIdentificacion.addEventListener('click', ()=> {
    if (isIdentified) {
        return alert('Identificación correcta');
    }
    alert('Usuario no identificado');
});


8. String literals

Los string literals son las comillas "patrás". Nos permiten concatenar de una forma LITERAL, asumiendo saltos de párrafo. En JS moderno, se utilizan en las SPA´s (Single Page Aplications) para crear componentes de HTML.

De esta manera, podemos hacer cosas tales como las que se ven en el ejemplo, en el que guardamos unos estilos en constantes, y los concatenamos a una propiedad style que añadiremos a nuestro elemento.

Los template literals nos permiten incluso meter condicionales. Podemos ver el ejemplo con un operador ternario, lo cual abrevia considerablemente la sintaxis

const parrafo = document.createElement('p');
const empresa = 'MyRuns';
const especialidad = 'RFID';
const background = 'springgreen';
const color = '#333';
const autorizado = true;
parrafo.innerText = `Esta empresa se llama ${empresa}, su 
                    especialidad es el ${especialidad}
                    `;
parrafo.style = `background:${autorizado ? background : 'tomato'}; color:${color}`;
document.body.append(parrafo);


9. Métodos con arrays

9.1. MAP

Los bucles forEach los utilizamos para señalar como anotación, que al sacarlo automáticamente desde VSCODE, nos incluirá la sintaxis de una función arrow, ya que se trata de un bucle que, para cada elemento iterado, lo utiliza como parámetro en cada ejecución de la función (mismo número de ejecuciones que elementos tenga el array).

Podemos hacer lo mismo que en el foreach, con una función map. Si además modificamos un poco el método Map, y lo guardamos en una constante, este método nos generará un nuevo array, "Mapeándonos" una serie de elementos. Para poder hacerlo correctamente, debemos especificar un return dentro del map, para que trate cada elemento y lo agrupe en el nuevo array.

En react utilizaremos mucho este método para procesar items que recibamos y convertirlos en elementos HTML. La idea es, de hecho, que el array se de de alta a través del método map(), y que a su vez, podamos darle formato insertándolo, por ejemplo, en un template literal para crear un elemento HTML.

const usuarios = ['Raúl', 'Rubén', 'Ander', 'Rodrigo', 'Joseba'];
usuarios.forEach( (usuario) => {
    console.log(usuario);
});
const usuariosMapa = ['Jose', 'Marcos', 'Irene', 'Mikel', 'Jokin'];
usuariosMapa.map( (usuarioMapa) => {
    console.log(usuarioMapa);
});
const usuariosAgrupados = usuariosMapa.map( (usuarioMapa) => {
    return `<ul>
                <li>
                Nombre usuario: ${usuarioMapa}
                </li>
            </ul>`; 
});
console.log(usuariosAgrupados);

9.2. FIND

Find es otro método muy utilizado en React. Sirve para buscar un array en un elemento.

const nameFound = usuarios.find((usuario)=> {
    if (usuario === 'Rodrigo') {
        return usuario;
    }
});
console.log('Hola Don ' + nameFound + '!');

9.3. FILTER

Find se utiliza bastante con React también a la hora de trabajar con Arrays. Permite crear un array con elementos que hayamos filtrado a través de una condición.

En una interfaz, por ejemplo, nos sirve para eliminar elementos de un array. Es como un "delete", por así decirlo.

const filteredName = usuarios.filter((usuario)=> {
    if (usuario !== 'Joseba') {
        return usuario;
    }
});
console.log(filteredName);

9.4. CONCAT

Como podemos deducir por el nombre del método, sirve para concatenar varios arrays. La concatenación se realiza dejando intactos los arrays que hayamos unido.

console.log('Usuarios concatenados: ' + usuarios.concat(usuariosMapa));
console.log(usuarios);
console.log(usuariosMapa);

9.5. SPREAD OPERATOR

Otra manera algo más actual y práctica de concatenar arrays, es el uso del operador spread. La sintaxis son tres puntos al añadir uno o varios arrays, al crear el nuevo.

El spread operator nos sirve también para concatenar objetos.

En ambos casos podemos observar por consola, que ha mantenido los elementos objetos/arrays originales aun después de haber generado el nuevo. console.log([...usuarios, ...usuariosMapa]);

const casa = {
    tipo: 'piso',
    habitaciones: 3,
    precio: 270000,
    barrio: 'Akartegi'
}
const habitantes = {
    tipo: 'familia',
    padre: 'Beñat',
    madre: 'Ane',
    hijos: ['Mikel', 'Alaitz']
}
const casaInfo = {
    ...casa,
    ...habitantes
}

console.log(casa);
console.log(habitantes);
console.log(casaInfo);


10. Ecmascript modules

Los módulos nos van a permitir traer incluir un fichero js en otro. En el que queremos traer tenemos que utilizar la sintaxis export al lado de la función o clase que vayamos a exportar. En el que importamos, usaremos import con la sintaxis que se muestra a continuación.

Podemos ser selectivos en lo que importamos, generando clases js que querramos traer de un fichero a otro, etc. Podría ser útil parar organizar nuestro código en clases, o si tuviésemos un js que sirviese de librería, y necesitásemos traernos métodos o clases específicas.

En este caso, nos traemos la función add(), si nos fijamos, la sintaxis que utilizamos es la que hemos visto para hacer destructuring.

RECORDEMOS! Si pulsamos cntrl + space, en VSCODE nos sacará los métodos y clases que podemos importar.

Habremos visto que, al intentar ejecutar el método suma, nos devuelve un error. Esto se debe a que la funcionalidad de los módulos todavía no está integrada en el intérprete de JavaScript del navegador. No obstante, al cargar el fichero que vaya a utilizar estas directivas en nuestro HTML, podemos darle un atributo type: "module"

RECORDEMOS! En VSCODE podemos hacer cntrl+click sobre el método, y nos llevará al fichero desde el que lo hemos importado.

Podemos traernos más de un método añadiéndolos en los corchetes de import y separándolos por comas.

Export nos vale igualmente para constantes, valores booleanos, arrays, objetos...

Otra opción interesante es export default al final del documento, que nos permitirá exportar por defecto los elementos que queramos. En este caso, ponemos que por defecto se exporte el título. Los módulos solo pueden exportar un elemento por defecto.

Al hacer la importación por defecto, después de la directiva import, obviamos los corchetes, y le ponemos el nombre que consideremos a la importación, como si fuera una constante.

Por último, comentar que en la sintaxis de JS puro, siempre ponemos la extensión .js para nombrar explícitamente los ficheros. En sintáxis que encontraremos en la mayoría de los frameworks de JavaScript moderno, no es necesario, con poner el nombre del fichero nos basta. Pero lo dicho, si ahora probamos a quitar la extensión, solo con el intérprete de js en el navegador web, nos devolverá un error.

import {sumar,multiplicar} from './modulo.js'; //>> Podemos probar a importarnos lo que necesitemos y mostrarlo por consola.
import miModulo from './modulo.js'; //>> Al hacer la importación por defecto, después de la directiva import, obviamos los corchetes, y le ponemos el nombre que consideremos a la importación, como si fuera una constante.
console.log(sumar(3,2));
console.log(multiplicar(5,4));
console.log(miModulo);


11. Optional changing

El optional chaining nos es muy útil cuando estamos trabajando con un equipo backend que nos está pasando datos en JSON, XML, etc. Se trata simplemente de poner un sígno de interrogación en el valor que estamos intentando extraer con la sintaxis de puntos para acceder a las propiedades/métodos de un objeto.

En la sintaxis de puntos, vamos navegando por las propiedades de un objeto. Si nos equivocamos en el último nivel, nos devolverá undefined, y si nos equivocamos en dos, nos dirá que no puede acceder a la propiedad de undefined, sacando un error por consola.

Es aquí donde podemos utilizar el optionar chainging. Si lo utilizamos en el código, en la propiedad características, veremos que ahora nos devuelve undefined.

Esto mismo lo podríamos hacer con un if, pero esta sintaxis, en muchos casos, nos facilitará las cosas.

const pokemon = {
    nombre: 'Charmander',
    atributos:{
        tipo:'Fuego',
        evolucion: 'Charizard',
    }

}

console.log('Que salga el siguiente error es deliberado. Podemos descomentar la línea 32 para verlo. Es para simular que nos hemos equivocado en los nombres de las propiedades.');
console.log(pokemon.caracteristicas?.entrenador);


12. Promesas y fetch API

12.1. FETCH Y THEN

Las promesas son la manera en la que programamos de manera asincronía, pidiéndole los datos a un backend. En react los datos vendrán de un backend, que a su vez, vendrán de una petición en el front.

Lo primero, es conocer la biblioteca fetch del navegador web. Esta biblioteca nos va a permitir realizar peticiones asíncronas a una URL. Vamos a utilizar una web bastante útil para hacer pruebas:

https://jsonplaceholder.typicode.com/

Si le pasamos una URL a fetch y lo mostramos por consola, veremos que nos devuelve una promesa.

Desde la aparición de ES15 como JavaScript moderno, las promesas son algo muy utilizado en programación asíncrona en el navegador, ya que te permiten no bloquear la carga del mismo, ni tampoco de la memoria de tu equipo, en caso de que sea una petición muy grande.

La sintaxis de una promesa, nos dice literalmente busca (fetch) en esta URL, entonces (then) ejecuta esta función.

Mientras realiza la "búsqueda" (es decir, la obtención de datos) el resto de la página o aplicación se seguirá ejecutando.

De hecho, **el método then, de la librería fetch, puede recibir como parámetro la respuesta, si le pasamos "response".

Si vemos el mensaje que hemos mostrado por consola, veremos que aunque lo hemos escrito después, en consola se mostrará en primer lugar, ya que el navegador interpreta primero nuestro fichero, y después la petición a la URL.

Ahora, tenemos que convertir esta respuesta en un dato que podamos leer por JavaScript, normalmente un JSON. Este método para convertir a JSON es parte de la API del navegador. No es de JS ni de ningún framework.

La parte que suele confundir a veces es la que viene ahora, ya que tenemos que volver a utilizar then() para meter a la propiedad data la respuesta que hemos procesado, formateándola a JSON. Podemos entender que vamos extrayendo los datos por pasitos, que el método then() va determinando para optimizar la carga asíncrona.

Si ahora miramos la consola, veremos que ya nos hemos traido todos los datos. Con ello, ya podemos pintarlo en la interfaz.

NOTA: Añadiéndole un pequeño parámetro por get podemos limitar los posts, de tal manera que no salgan los 100.

const ul = document.createElement('ul');
const li = document.createElement('li');
const dataDiv = document.querySelector('#obtainedData');
dataDiv.append(ul);



fetch('https://jsonplaceholder.typicode.com/posts?_start=0&_limit=5').then((response) => {
    return response.json()
}).then((data) => {
    console.log(data);
    data.forEach((post) => {
        let title = post.title;
        let body = post.body;
        dataDiv.querySelector('ul').innerHTML += `
            <li>
                <h3><strong> ${title} </strong></h3>
                <p>${body}</p>
            </li>`;
    });
});

console.log('...Obteniendo los datos, la página se está cargando');

12.2. ASYNC / AWAIT

El uso de funciones determinadas como asíncronas utilizando la palabra reservada async, es más actual y nos lo encontraremos de manera habitual.

En este caso, hacemos un poco lo mismo, pero con una sintaxis que se puede hacer algo más legible. El código, con la palabra reservada await (dentro de una función que se ha determinado como asíncrona con la palabra reservada async) está diciendo:

1 -> "Espero (await) una búsqueda en esta URL" se guarda en la constante response 2 -> "Espero (await) una respuesta y la convierto a JSON" se guarda en la constante data

Cuando la espera de estos dos requisitos termina, pinto la interfaz. Esta función la tenemos que ejecutar.

async function getData() {
    const response = await fetch('https://jsonplaceholder.typicode.com/photos?_start=0&_limit=5');
    const data = await response.json();
    console.log(data);
    data.forEach((post) => {
        let title = post.title;
        let url = post.url;
        dataDiv.querySelector('ul').innerHTML += `
            <li>
                <h3> <strong> ${title} </strong> </h3>
                <img style="max-width:400px" src="${url}" alt="">
            </li>`;
    });
}
getData();